#ifndef LOG_STREAM_H_
#define LOG_STREAM_H_

#include <string>

#include "netlib/base/log_buffer.h"
#include "netlib/base/noncopyable.h"
// #include "netlib/base/string_piece.h"

namespace netlib {

class LogStream : NonCopyable {
public:
	using Buffer = detail::Buffer<detail::kSmallBuffer>;
	using OutputFunc = std::function<void(const char*, size_t)>;
	using FlushFunc = std::function<void()>;
	// typedef void (*OutputFunc)(const char*, size_t);
	struct KnowSize {
		KnowSize(const char* d, int l) : data_(d), len_(l) {}
		const char* data_;
		int len_;
	};

	struct BaseName : Copyable {
		template <int N> explicit BaseName(const char (&name)[N]) : data_(name), size_(N - 1) {
			const char* slash = strrchr(data_, '/');
			if (slash) {
				data_ = slash + 1;
				size_ -= static_cast<int>(slash - name + 1);
			}
		}
		explicit BaseName(const char* name) : data_(name) {
			const char* slash = strrchr(data_, '/');
			if (slash) {
				data_ = slash + 1;
			}
			size_ = static_cast<int>(strlen(data_));
		}

		const char* data_;
		int size_;
	};

	LogStream() = default;
	~LogStream() = default;

	LogStream& operator<<(bool val);
	LogStream& operator<<(char val);
	LogStream& operator<<(int16_t val);
	LogStream& operator<<(int val);
	LogStream& operator<<(unsigned val);
	LogStream& operator<<(int64_t val);
	LogStream& operator<<(uint64_t val);
	LogStream& operator<<(float val);
	LogStream& operator<<(double val);
	LogStream& operator<<(const char* str);
	LogStream& operator<<(const std::string_view& str) {
		data_.Append(str.data(), str.size());
		return *this;
	}
	LogStream& operator<<(const std::string& str);
	LogStream& operator<<(std::string&& str);
	LogStream& operator<<(KnowSize&& obj);
	LogStream& operator<<(const BaseName& obj);
	// LogStream& operator<<(const Buffer& buf);
	// LogStream& operator<<(Buffer&& buf);

	void Output() { output_func(data_.Data(), data_.Size()); }
	static void Flush() { flush_func(); }

	static void SetOutputFunc(OutputFunc&& func) { output_func = func; }
	static void SetFlushFunc(FlushFunc&& func) { flush_func = func; }

private:
	static void DefaultOutput(const char* data, size_t len) {
		size_t n = fwrite(data, 1, len, stdout);
		assert(n == len);
	}
	static void DefaultFlush() { fflush(stdout); }

	Buffer data_;
	static OutputFunc output_func;
	static FlushFunc flush_func;
};

} // namespace netlib

#endif // LOG_STREAM_H_
